#include <QtCore>
#include <QtWidgets>
#include "BuildBinDialog.h"
#include "ui_BuildBinDialog.h"
#include "Commfunc.h"
#include "S19ToBin.h"

//---------------------------------------------------------------------------------------
#define TMP_BIN "tmp.bin"
//---------------------------------------------------------------------------------------
BuildBinDialog::BuildBinDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::BuildBinDialog)
{
    ui->setupUi(this);

    QString str = "二进制文件(*.bin *.s19)";
    ui->lineEditSrc->SetActionType(LineEditWithAction::GetFile, &str);
    str = "S19文件(*.s19)";
    ui->lineEditS19->SetActionType(LineEditWithAction::GetFile, &str);
    str = "二进制文件(*.bin)";
    ui->lineEditDest->SetActionType(LineEditWithAction::GetSaveFile, &str);
    ui->lineEditBIN->SetActionType(LineEditWithAction::GetSaveFile, &str);

    ui->lineEditBin0->SetActionType(LineEditWithAction::GetFile, &str);
    ui->lineEditBin1->SetActionType(LineEditWithAction::GetFile, &str);
    ui->lineEditBin2->SetActionType(LineEditWithAction::GetFile, &str);
    ui->lineEditCombine->SetActionType(LineEditWithAction::GetSaveFile, &str);

    ui->lineEditBinIn->SetActionType(LineEditWithAction::GetFile, &str);
    ui->lineEditBinOut->SetActionType(LineEditWithAction::GetSaveFile, &str);

    ui->lineEdit_path_sum->SetActionType(LineEditWithAction::GetFile, &str);

    ftype = FILE_TYPE_BIN;
}

BuildBinDialog::~BuildBinDialog()
{
    delete ui;
}
//---------------------------------------------------------------------------------------
int BuildBinDialog::GetDevTypeFromBinFile(const QString &infile, uint32_t start, char *devtype)
{
    QFile in(infile);

    if (!in.open(QFile::ReadOnly)) return -1;

    uint16_t offset;
    in.seek(6);
    in.read((char*)&offset, sizeof(offset));
    offset = cpu_to_be16(offset);
    offset -= start;
    in.seek(offset);
    in.read(devtype, DEVTYPE_LEN);
    in.close();
    return 0;
}
//---------------------------------------------------------------------------------------
int BuildBinDialog::GetInputFileData(const QString &in, QByteArray &b, uint8_t paddata, int padsize)
{
    QFile f(in);

    if (!f.open(QIODevice::ReadOnly)) return -1;
    fsize = f.size();
    if ((padsize > 0) && (fsize % padsize))
    {
        fsize += padsize - (fsize % padsize);
    }
    b.resize(fsize);
    f.read(b.data(), f.size());
    memset(b.data()+f.size(), paddata, fsize - f.size());

    if (ui->groupBoxInfoIn->isChecked())
    {
        memset((uint8_t*)(b.data()+ui->spinBoxFileSize->value()), 0x00, 4);
        put_val(b.size(), (uint8_t*)(b.data()+ui->spinBoxFileSize->value()),
                ui->comboBoxFileSizeBytes->currentText().toInt(),
                ui->checkBoxFileSizeBigEndian->isChecked());
        str2arr(ui->lineEditDevType->text().toLatin1(),
                (uint8_t*)(b.data()+ui->spinBoxDevType->value()),
                ui->comboBoxDevTypeBytes->currentText().toInt());
        memset((uint8_t*)(b.data()+ui->spinBoxFileCrc->value()), 0x00, 2);
        fcrc = calc_crc16(0, b.data(), b.size());
        put_val(fcrc, (uint8_t*)(b.data()+ui->spinBoxFileCrc->value()),
                ui->comboBoxFileCrcBytes->currentText().toInt(),
                ui->checkBoxFileCrcBigEndian->isChecked());
    }
    else
    {
        fcrc = calc_crc16(0, b.data(), b.size());
    }
    f.close();

    return 0;
}
//---------------------------------------------------------------------------------------
void BuildBinDialog::on_pushButtonPackage_clicked()
{
    QString src;

    ui->textBrowser->clear();

    if (ui->lineEditSrc->text().isEmpty() || ui->lineEditDest->text().isEmpty())
    {
        QMessageBox::critical(this, "错误", "请选择输入输出文件");
        return;
    }

    QFileInfo fileinfo(ui->lineEditSrc->text());
    ftype = !fileinfo.suffix().compare(QString("s19"), Qt::CaseInsensitive)
            ? FILE_TYPE_S19 : FILE_TYPE_BIN;

    if (ftype == FILE_TYPE_BIN && ui->lineEditDevType->text().isEmpty())
    {
        QMessageBox::critical(this, "错误", "请设备类型信息");
        return;
    }
    if (ui->lineEditSoftVer->text().isEmpty())
    {
        QMessageBox::critical(this, "错误", "请填写版本信息");
        return;
    }

    src = ui->lineEditSrc->text();
    int padsize = ui->comboBoxPad->currentText().toInt();

    if (ftype == FILE_TYPE_S19)
    {
        uint32_t startAddr = 0xFFFFFFFF;
        int ret = ConvertS19ToBin(ui->lineEditSrc->text(),
                            TMP_BIN,
                            startAddr,
                            0,
                            0xFF);
        if (ret < 0)
        {
            QMessageBox::critical(this, "错误", "转换S19文件失败");
            return;
        }
        src = TMP_BIN;

        if (GetDevTypeFromBinFile(src, startAddr, devtype) < 0) return;
        char str[0x100];
        ui->lineEditDevType->setText(arr2str(devtype, sizeof(devtype), str, sizeof(str)));
    }

    QByteArray b;
    if (GetInputFileData(src, b, 0xFF, padsize) < 0)
    {
        QMessageBox::critical(this, "错误", "读取输入文件失败");
        return;
    }

    if (DEVTYPE_LEN != str2arr(ui->lineEditDevType->text().toLatin1(), devtype, DEVTYPE_LEN))
    {
        QMessageBox::critical(this, "错误", "设备类型格式不对");
        return;
    }

    if (DoPackageBin(b,
                     ui->lineEditDest->text(),
                     ui->lineEditSoftVer->text().toLatin1()) < 0)
    {
        QMessageBox::critical(this, "错误", "打包失败");
        return;
    }

    remove(TMP_BIN);

    ui->textBrowser->append(QString("文件大小：%1h").arg(QString::number(fsize,16)));
    ui->textBrowser->append(QString("文件CRC：%1h").arg(QString::number(fcrc, 16)));
    ui->textBrowser->append(QString("设备类型：%1").arg(ui->lineEditDevType->text()));
    ui->textBrowser->append(QString("软件版本：%1").arg(ui->lineEditSoftVer->text()));

    QMessageBox::information(this, "恭喜", "打包成功");
}
//---------------------------------------------------------------------------------------
int BuildBinDialog::DoPackageBin(const QByteArray &b, const QString &out,
                                 const char *softver
                                 )
{
    const char *dev = "device type:";
    const char *ver = "soft ver:";
    const char *crc = "file crc:";
    const char *size= "file size:";
    const char *prog= "program start:";


    char tmp[0x40];
    //remove(out);

    QFile fpw(out);
    if (!fpw.open(QFile::WriteOnly)) return -1;

    /* device type */
    fpw.write(dev, strlen(dev));
    arr2str_complex(devtype, DEVTYPE_LEN, tmp, sizeof(tmp), 0);
    fpw.write(tmp, strlen(tmp)+1);

    /* soft version */
    fpw.write(ver, strlen(ver));
    fpw.write(softver, strlen(softver)+1);

    /* crc */
    uint16_t _crc;
    fpw.write(crc, strlen(crc));
    _crc = cpu_to_be16(fcrc);
    snprintf(tmp, sizeof(tmp), "%04X%c", _crc, 0);
    fpw.write(tmp, strlen(tmp)+1);

    /* size */
    fpw.write(size, strlen(size));
    snprintf(tmp, sizeof(tmp), "%X%c", b.size(), 0);
    fpw.write(tmp, strlen(tmp)+1);

    /* data */
    fpw.write(prog, strlen(prog));
    fpw.write(b);

    fpw.close();
    return 0;
}
//---------------------------------------------------------------------------------------
void BuildBinDialog::on_pushButtonCvtS19ToBIN_clicked()
{
    if (ui->lineEditS19->text().isEmpty() || ui->lineEditBIN->text().isEmpty())
    {
        QMessageBox::critical(this, "错误", "请选择输入输出文件");
        return;
    }

    uint32_t start = 0xFFFFFFFF;
    int ret = ConvertS19ToBin(ui->lineEditS19->text(),
                              ui->lineEditBIN->text(),
                              start,
                              0,
                              0xFF);
    if (ret < 0)
    {
        QMessageBox::critical(this, "错误", "转换S19文件失败");
        return;
    }
    QMessageBox::information(this, "恭喜", "转换成功");
}
//---------------------------------------------------------------------------------------
int BuildBinDialog::DoCombineBin(const QList<CombinePar> &combines, const QString &outfile)
{
    QFile outf(outfile);
    if (!outf.open(QIODevice::WriteOnly)) return -1;

    for (int i = 0; i != combines.count(); i++)
    {
        const CombinePar &combine = combines.at(i);

        if (combine.filename.isEmpty()) continue;

        QFile f(combine.filename);
        if (!f.open(QIODevice::ReadOnly))
        {
            outf.close();
            return -1;
        }

        if (combine.offset > outf.size())
        {
            QByteArray b;
            b.resize(combine.offset - outf.size());
            b.fill(0xFF);
            outf.write(b);
        }
        outf.seek(combine.offset);
        outf.write(f.readAll());
        f.close();
    }

    outf.close();
    return 0;
}

void BuildBinDialog::on_pushButtonCombine_clicked()
{
    QList<CombinePar> combines;

    CombinePar combine;
    combine.filename = ui->lineEditBin0->text();
    combine.offset = 0;
    combines.append(combine);
    combine.filename = ui->lineEditBin1->text();
    combine.offset = ui->spinBoxBin1Off->value();
    combines.append(combine);
    combine.filename = ui->lineEditBin2->text();
    combine.offset = ui->spinBoxBin2Off->value();
    combines.append(combine);

    int ret = DoCombineBin(combines, ui->lineEditCombine->text());
    if (ret < 0)
    {
        QMessageBox::critical(this, "错误", "合并文件失败");
        return;
    }
    QMessageBox::information(this, "恭喜", "合并成功");
}
//---------------------------------------------------------------------------------------
void BuildBinDialog::on_pushButtonCut_clicked()
{
    int off, size;

    if (ui->lineEditBinIn->text().isEmpty() || ui->lineEditBinOut->text().isEmpty())
    {
        QMessageBox::critical(this, "错误", "请选择输入输出文件");
        return;
    }

    QFile in(ui->lineEditBinIn->text()), out(ui->lineEditBinOut->text());
    if (!in.open(QIODevice::ReadOnly) || !out.open(QIODevice::WriteOnly))
    {
        QMessageBox::critical(this, "错误", "文件打开失败");
        goto out;
    }

    off = ui->spinBoxBinOff->value();
    size = ui->spinBoxBinSize->value();
    if (in.size() < off)
    {
        QMessageBox::critical(this, "错误", "偏移超过输入文件的大小");
        goto out;
    }

    in.seek(off);
    out.write(in.read(size));

    QMessageBox::information(this, "恭喜", "截取成功");
out:
    in.close();
    out.close();
}
//---------------------------------------------------------------------------------------
void BuildBinDialog::on_pushButton_checksum_clicked()
{
    if (ui->lineEdit_path_sum->text().isEmpty())
    {
        QMessageBox::critical(this, "错误", "请选择输入输出文件");
        return;
    }

    QFile in(ui->lineEdit_path_sum->text());
    if (!in.open(QIODevice::ReadOnly))
    {
        QMessageBox::critical(this, "错误", "文件打开失败");
        return;
    }

    QByteArray d = in.readAll();
    uint32_t sum = checksum_u32(d.data(), d.size());

    ui->lineEdit_sum->setText(QString::asprintf("%08X", sum));// :number(sum, 16));

    in.close();
}
//---------------------------------------------------------------------------------------
